/** *
 *This version fixes two important bugs
 Are
 One. Time formatting exception caused by ‘moment’
 two. When using ‘TimeDropdown’ to lose focus, the missing focus function error is missing
 In addition: The text definition of the default button has been optimized. You can use ‘buttionName’ in the reference component to customize it, and set the default time zone of the component to ‘UTC’.
 A new style has been added to ‘TimeDropdown’ to prevent scrolling.
 Renamed the components, they are the perfect timing
 Author: zurich lee
 Company: Guangzhou Tuchuang Computer Software Development Co., Ltd.
 Time: 2020/05
 本版修复了重要两个错误
 分别是
 一。使用‘moment’ 引发的时间格式化异常
 二。使用‘TimeDropdown’ 在失去焦点时，缺少失去焦点函数的错误
 另外：对默认按钮的文字定义进行了优化，可以在引用组件中使用‘buttionName’进行自定义，同时对组件默认时区设定为‘UTC’。
 对‘TimeDropdown’ 加入了新的样式，禁止滚动条滚动。
 对组件进行了重新的命名，他们是完美的时间选择
 作者 :  zurich lee
 公司：广州图创计算机软件开发有限公司
 时间：2020/05
 */
import React, { Component, Fragment } from 'react';

import { FormattedMessage, intlShape } from 'react-intl';
import PropTypes from 'prop-types';

import TetherComponent from 'react-tether';
import RootCloseWrapper from 'react-overlays/lib/RootCloseWrapper';
import moment from 'moment-timezone';
import contains from 'dom-helpers/query/contains';
import debounce from 'lodash/debounce';
import uniqueId from 'lodash/uniqueId';
import {
  IconButton,

  TextField,

} from '@folio/stripes/components';
import TimePerfectDropdown from './TimePerfectDropdown';
import css from './TimePerfectDropdown.css';

const propTypes = {
  autoFocus: PropTypes.bool,
  disabled: PropTypes.bool,
  id: PropTypes.string,
  input: PropTypes.object,
  label: PropTypes.node,
  locale: PropTypes.string,
  meta: PropTypes.object,
  onChange: PropTypes.func,
  readOnly: PropTypes.bool,
  required: PropTypes.bool,
  screenReaderMessage: PropTypes.string,
  showTimepicker: PropTypes.bool,
  tether: PropTypes.object,
  timeZone: PropTypes.string,
  value: PropTypes.string,
  buttonName:PropTypes.string,
  defaultHour:PropTypes.string,
  defaultMinute:PropTypes.string,
  isClear:PropTypes.bool,
};
const contextTypes = {
  intl: intlShape
};
const defaultProps = {
  defaultHour:'17',
  defaultMinute:'00',
  isClear:false,
  autoFocus: false,
  screenReaderMessage: '',
  tether: {
    classes: {
      element: css.tetherElement
    },
    attachment: 'top center',
    renderElementTo: null,
    targetAttachment: 'bottom center',
    optimizations: {
      gpu: false,
    },
    constraints: [{
      to: 'window',
      attachment: 'together',
    },
    {
      to: 'scrollParent',
      pin: true,
    },
    ],
  },
};
class TimePerfectChoice extends Component {
  constructor(props) {
    super(props);
    this.textfield = React.createRef();
    this.picker = null;
    this.container = null;
    this.srSpace = null;
    this.timeZone = props.timeZone || 'Asia/Shanghai';
    this.locale = props.locale || 'zh-cn';
    moment.locale(this.locale);
    this._timeFormat = 'HH:mm';
    this.formats = ['YYYY-MM-DD HH:mm'];

    let inputValue = '';
    let presentedValue = null;

    if (this.props.value === this.props.passThroughValue) {
      presentedValue = this.props.passThroughValue;
      inputValue = moment.tz(this.timeZone).format(this._timeFormat);
    } else {
      inputValue = moment(this.props.value, this._timeFormat, this.timeZone).format(this._timeFormat);
    }

    this.state = {
      presentedValue,
      timeString: inputValue,
      showTimepicker: this.props.showTimepicker || false,
      timeFormat: this._timeFormat,
      srMessage: '',

    };

    if (props.id) {
      this.testId = props.id;
    } else {
      this.testId = uniqueId('timepick-');
    }
  }

    cleanForScreenReader=(str) => {
      const newString = str.replace(/H/g, 'H ').replace(/m/g, 'm ').replace(/s/g, 's ');
      return newString;
    }

    getPresentationValue=() => {
      if (this.state.presentedValue !== null) {
        return this.state.presentedValue;
      }
      return this.state.timeString;
    }


    componentWillReceiveProps(nextProps, nextContext) { // eslint-disable-line react/no-deprecated
      let inputValue;
      let presentedValue = null;
      if (nextProps.input) { // if we're using redux-form....
        // if value has changed....
        if (nextProps.input.value !== '' && nextProps.input.value !== this.props.input.value) {
          if (nextProps.input.value === nextProps.passThroughValue) {
            presentedValue = nextProps.passThroughValue;
            inputValue = moment.tz(this.timeZone).format(this._timeFormat);
            this.setState({
              presentedValue,
              timeString: inputValue,
            });
          }
          // if value is blank....
        } else if (nextProps.input.value === '' && nextProps.input.value !== this.props.input.value) {
          inputValue = '';
          this.setState({
            presentedValue,
            timeString: inputValue,
          });
        }
      } else if (this.props.value !== '' && this.props.value !== nextProps.value) {
        if (nextProps.value === this.props.passThroughValue) {
          inputValue = moment.tz(this.timeZone).format(this._timeFormat);
          presentedValue = this.props.passThroughValue;
        } else {
          inputValue = moment.tz(this.timeZone).format(this._timeFormat);
        }
        this.setState({
          presentedValue,
          timeString: inputValue,
        });
      }

      // adjust displayed time format for a change in locale
      // if (nextContext.intl.locale !== this.context.intl.locale) {
      //   moment.locale(nextContext.intl.locale);
      //   this.setState(prevState => {
      //     let newTimeString = prevState.timeString;
      //     if (newTimeString !== '') {
      //       newTimeString = moment.tz(newTimeString, prevState.timeFormat, this.timeZone).format(this._timeFormat);
      //     }
      //
      //     return {
      //       presentedValue: null,
      //       timeFormat: this._timeFormat,
      //       timeString: newTimeString,
      //     };
      //   });
      // }
    }

    // examine the passed time format to determine 12 or 24 hour format.
    deriveHoursFormat=(fmt) => {
      let _fmt;
      if (!fmt) {
        _fmt = this.state.timeFormat;
      } else {
        _fmt = fmt;
      }
      const ampmRE = new RegExp(/A/);
      if (ampmRE.test(_fmt)) {
        return '12';
      }
      return '24';
    }

    getLocalTime=(t, f) => {
      if (t) {
        return moment(t).format(f);
      }
      return moment().format(this._timeFormat);
    }

    handleFieldClick=() => {
      this.toggleTimepicker();
    }

    handleRootClose=(e) => {
      // used by <RootCloseWrapper> to determine whether or not it should hide the picker.
      // it should determine that the element that's focused is outside of the datepicker's containing div and
      // the calendar widget (this.picker).
      if (this.state.showTimepicker) {
        if (!contains(this.container, e.target)) {
          if (!this.pickerIsFocused()) {
            this.hideTimepicker();
          }
        }
      }
    }

    showTimepicker=() => {
      this.setState({
        showTimepicker: true,
      });
    }

    hideTimepicker=(cb) => {
      this.setState({
        showTimepicker: false,
      }, cb);
    }

    toggleTimepicker=() => {
      const current = this.state.showTimepicker;
      this.setState({
        showTimepicker: !current,
      });
    }

    handleKeyDown=(e) => {
      if (this.picker) {
        // const formattedDate = curDate.format(this._timeFormat);
        // let timeString;
        switch (e.keyCode) {
          case 40: // down
            e.preventDefault();
            break;
          case 38: // up
            e.preventDefault();
            break;
          case 37: // left
            break;
          case 39: // right
            break;
          case 27: // escape
            e.preventDefault();
            this.hideTimepicker();
            break;
          case 34: // pgDn
            e.preventDefault();
            break;
          case 33: // pgUp
            e.preventDefault();
            break;
          case 13: // enter
            e.preventDefault();
            if (this.state.showTimepicker) {
              // this.handleSetTime(e, curDate, formattedDate);
            } else {
              this.showTimepicker();
            }
            // timeString = `${formattedDate} selected`;
            break;
          case 9: // tab
            this.hideTimepicker();
            break;
          default:
        }
        // this.informScreenReader(`${timeString}`);
      } else {
        switch (e.keyCode) {
          case 13: // enter
          case 40: // down
            e.preventDefault();
            this.showTimepicker();
            break;
          default:
        }
      }
    }

    handleSetTime=(e, hours, minutes, ampm) => {
      if (e === null || e.type === 'click' || e.type === 'keydown') {
        if (e) { e.preventDefault(); }

        if (this.props.onChange) { this.props.onChange(e); }
        this.textfield.current.getInput().focus();

        const tString = `${hours}:${minutes}`;


        this.setState({
          presentedValue: null,
          timeString: tString,
        });

        // standardizedTime = this.standardizeTime(`${hours}:${minutes} ${ampm}`);
        if (this.props.input) {
          this.props.input.onChange(tString);
          if (this.props.onChange) {
            this.props.onChange(e, tString);
          }
        } else if (this.props.onChange) {
          this.props.onChange(e, tString);
        }
      } else if (e.type === 'change') {
        // if e.target.value is even a substring of props.passThroughValue, pass it through...


        // be sure that value is parseable as a date in the required format...
        // the boolean parameter suppresses moment's deprecation warning,
        // preventing it from attempting a parse using independable js Date object.
        this.setState({
          timeString: e.target.value,
        });

        if (this.props.onChange) { this.props.onChange(e); }
        if (this.props.input) {
          if (moment(e.target.value, this._timeFormat, true).isValid()) {
            this.setState({
              timeString: e.target.value,
            });
            const tString = `${hours}:${minutes}`;

            // convert date to ISO 8601-ish format for backend (everything after the 'T')
            // if (!ampm) {
            //     standardizedTime = this.standardizeTime(`${hours}:${minutes}`);
            // } else {
            //     standardizedTime = this.standardizeTime(`${hours}:${minutes} ${ampm}`);
            // }
            // redux-form handlers take the value rather than the event...
            this.props.input.onChange(tString);
            if (this.props.onChange) {
              this.props.onChange(e, tString);
            }
          }
        }
      }
    }

    handleFieldChange=(e) => {
      if (e.target.value === '') {
        this.clearTime();
        return;
      }
      const ptREx = new RegExp(`^${e.target.value}`, 'i');
      if (ptREx.test(this.props.passThroughValue)) {
        if (this.props.onChange) { this.props.onChange(e); }
        if (this.props.input) {
          this.props.input.onChange(e.target.value);
        }
        this.setState({
          presentedValue: e.target.value,
        });
      } else {
        let hoursMins = e.target.value.split(/[:\s]/g);
        hoursMins = hoursMins.filter(val => val); // remove any blank items...
        if (hoursMins.length > 0) {
          let hours = hoursMins[0];
          const intHours = parseInt(hours, 10);
          if (intHours < 10) {
            hours = `0${intHours}`;
          }

          if (hoursMins.length < 2) {
            this.handleSetTime(e, hours, '00');
            return;
          }
          let mins = hoursMins[1];
          let ampm;
          if (hoursMins.length < 3) {
            if (mins.length > 2) { // handle 3:00PM (no space between minutes and meridiem)
              if (this.deriveHoursFormat() === '12') {
                ampm = 'PM';
                if (/[aA]/.test(mins)) {
                  ampm = 'AM';
                }
                const intMins = parseInt(mins, 10);
                if (intMins < 10) {
                  mins = `0${intMins}`;
                } else {
                  mins = intMins.toString();
                }
                this.handleSetTime(e, hours, mins, ampm);
              }
            }
            this.handleSetTime(e, hours, mins);
            return;
          }
          ampm = hoursMins[2];
          this.handleSetTime(e, hours, mins, ampm);
        }
      }
    }

    clearTime=(e) => {
      this.setState({
        presentedValue: null,
        timeString: '',
      });
      if (this.props.onChange) { this.props.onChange(e); }
      if (this.props.input) {
        this.props.input.onChange('');
      }
      this.textfield.current.getInput().focus();
    }

    cleanForScreenReader=(str) => {
      const newString = str.replace(/H/g, 'H ').replace(/m/g, 'm ').replace(/s/g, 's ');
      return newString;
    }

    informScreenReader=(str) => {
      this.setState({ srMessage: str });
    }

    pickerIsFocused=() => {
      if (contains(this.container, document.activeElement) &&
            document.activeElement !== document.body) {
        return contains(this.picker.getDOMContainer(), document.activeElement);
      }
      return false;
    }

    hideOnBlur=(e) => {
      if (this.pickerIsFocused()) {
        if (this.props.input) {
          this.props.input.onBlur(e);
        }
        this.hideTimepicker();
        this.setState({ srMessage: '' });
      }
    }

    standardizeTime=(time) => {
      const isoTime = moment.tz(time, 'HH:mm A', this.timeZone).toISOString();
      const timeSplit = isoTime.split('T');
      return timeSplit[1];
    }

    handlePickTime=(timeObject) => {
      this.handleSetTime(null, timeObject.hour, timeObject.minute, timeObject.ampm);
      this.hideTimepicker();
    }

    getPresentationValue=() => {
      if (this.state.presentedValue !== null) {
        return this.state.presentedValue;
      }
      return this.state.timeString;
    }


    render() {
      const {
        input,
        screenReaderMessage,
        label,
        readOnly,
        required,
        disabled,
        tether,
        defaultHour,
        defaultMinute,
        isClear
      } = this.props;
      let hour = defaultHour;
      let minute = defaultMinute;

      if (typeof input !== 'undefined') {
        if (input.value !== '') {
          hour = moment('1979-01-01 ' + input.value).format('HH');
          minute = moment('1979-01-01 ' + input.value).format('mm');
        }
      }
      const screenReaderFormat = this.cleanForScreenReader(this._timeFormat);
      const mergedTetherProps = Object.assign({}, TimePerfectChoice.defaultProps.tether, tether);

      let ariaLabel;
      if (readOnly || disabled) {
        ariaLabel = `${label}`;
      } else {
        // eslint-disable-next-line max-len
        ariaLabel = `${label} format is ${screenReaderFormat} use arrow keys to navigate dates. Enter key to confirm. ${screenReaderMessage}`;
      }

      let endElement;
      if (this.state.timeString !== '' || this.state.presentedValue !== null) {
        endElement = (
          <Fragment>
            <FormattedMessage id="ui-party.stripes-components.clearFieldValue">
              {clearFieldValueAriaLabel => (
                <IconButton
                  key="clearButton"
                  onClick={this.clearTime}
                  aria-label={clearFieldValueAriaLabel}
                  id={`timepicker-clear-button-${this.testId}`}
                  tabIndex="-1"
                  icon="times-circle-solid"
                />
              )}
            </FormattedMessage>
            <FormattedMessage id="ui-party.stripes-components.showOrHideTimepicker">
              {showOrHideTimepickerAriaLabel => (
                <IconButton
                  key="timepickerButton"
                  onClick={this.toggleTimepicker}
                  onKeyDown={this.handleKeyDown}
                  aria-label={showOrHideTimepickerAriaLabel}
                  id={`timepicker-toggle-button-${this.testId}`}
                  tabIndex="-1"
                  icon="clock"
                />
              )}
            </FormattedMessage>
          </Fragment>
        );
      } else {
        endElement = (
          <FormattedMessage id="ui-party.stripes-components.showOrHideTimepicker">
            {showOrHideTimepickerAriaLabel => (
              <IconButton
                key="timepickerButton"
                onClick={this.toggleTimepicker}
                onKeyDown={this.handleKeyDown}
                aria-label={showOrHideTimepickerAriaLabel}
                id={`timepicker-toggle-button-${this.testId}`}
                tabIndex="-1"
                icon="clock"
              />
            )}
          </FormattedMessage>
        );
      }

      let textfield;
      if (this.props.input && this.props.meta) {
        let isoTime = null;
        if (input.value) {
          isoTime = input.value.substring(0, 5);
        }

        textfield = (
          <TextField
            ref={this.textfield}
            input={input}
            value={isoTime}
            label={label}
            aria-label={ariaLabel}
            endControl={(isClear) ? endElement : null}
            placeholder={` ${this._timeFormat}`}
            onKeyDown={(readOnly || disabled) ? null : this.handleKeyDown}
            onClick={(readOnly || disabled) ? null : this.handleFieldClick}
            onBlur={this.hideOnBlur}
            meta={this.props.meta}
            onChange={this.handleFieldChange}
            readOnly
            disabled={disabled}
            id={this.testId}
            required={required}
            hasClearIcon={false}
            autoFocus={this.props.autoFocus}
            autoComplete="off"
          />
        );
      } else {
        textfield = (
          <TextField
            ref={this.textfield}
            label={label}
            value={this.getPresentationValue()}
            aria-label={ariaLabel}
            endControl={(isClear) ? endElement : null}
            placeholder={this._timeFormat.toUpperCase()}
            onKeyDown={(readOnly || disabled) ? null : this.handleKeyDown}
            onBlur={this.hideOnBlur}
            onClick={(readOnly || disabled) ? null : this.handleFieldClick}
            onChange={this.handleFieldChange}
            readOnly={readOnly}
            disabled={disabled}
            id={this.testId}
            required={required}
            hasClearIcon={false}
            autoFocus={this.props.autoFocus}
          />
        );
      }

      return (
        <TetherComponent {...mergedTetherProps}>
          <div style={{ position: 'relative', width: '100%' }} ref={(ref) => { this.container = ref; }}>
            <div
              aria-live="assertive"
              aria-relevant="additions"
              className={css.srOnly}
              ref={(ref) => { this.srSpace = ref; }}
            >
              <div>{this.state.srMessage}</div>
            </div>
            {textfield}
          </div>
          <RootCloseWrapper onRootClose={this.handleRootClose}>
            <TimePerfectDropdown
              defaultHour={hour}
              defaultMinute={minute}
              buttonName="确 定"
              visible={this.state.showTimepicker}
              onSetTime={this.handlePickTime}
              selectedTime={this.state.timeString}
              timeFormat={this.state.timeFormat}
              ref={(ref) => { this.picker = ref; }}
              mainControl={this.textfield.current}
              onKeyDown={this.handleKeyDown}
              onBlur={this.hideTimepicker}
              locale={this.locale}
              onNavigation={this.handleDateNavigation}
              id={this.testId}
              onClose={this.hideTimepicker}
            />
          </RootCloseWrapper>
        </TetherComponent>
      );
    }
}


TimePerfectChoice.propTypes = propTypes;
TimePerfectChoice.contextTypes = contextTypes;
TimePerfectChoice.defaultProps = defaultProps;
export default TimePerfectChoice;
